/***************************************************************************
 *   Copyright (C) 2007 by Alexandre Masselot,,,   *
 *   alex@alex-laptop   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should havonste received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef BOLPEAK_H
#define BOLPEAK_H

#include <iostream>

#ifdef HAVE_BOOST_SERIALIZATION
#include <boost/serialization/export.hpp>
#include <boost/serialization/nvp.hpp>
#endif

#ifdef HAVE_PROTOBUF
#ifdef _MSC_VER
// we don't create DLLs and thus don't need to pay attention to this warning
#pragma warning( disable: 4251 )
#endif
#include "runlcmsms.pb.h"
#endif

#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>

using namespace std;
namespace bol {



 /**
  the type for charge mask (ready to be extended to bitset or whatever if need comes to very higly charged peaks)
  */
  typedef unsigned long int Pchargemask;

  class Peak{
/**
    the basic peak (just contains a m/z & chargemask)

    One thing is to be carfult about the chargemask. having it to 12 does not means that the peak is charged 12+!! looking at the bit representation of 12 says that the peak is charged 2+ OR 3+

    @author Alexandre Masselot,,, <alex@alex-laptop>
 */
    private:
      const double m_moz;
      Pchargemask m_chargemask;
      unsigned int *m_charges;
      static boost::regex *s_pattern_charges;
      static boost::cmatch *s_matches_charges;

      void buildChargesList();

#ifdef HAVE_BOOST_SERIALIZATION
      // *** BOOST serialisation code ***
      friend class boost::serialization::access; // gives access to serialisator
      template<class Archive> void serialize ( Archive & ar, const unsigned int version ) const
      {
        // base class serialisation - warning, as there are several variable, the order is critical...
        // ... well not for XML. But for most other archive types.
        ar & boost::serialization::make_nvp("MoZ", m_moz);
        // FIXME charges will probably require a split serializer
      }
#endif

    public:
    /**
       *
       * @param m  m/z
       * @param c charge mask bitset (see chargemask2string function to see how this works) - default is 0
     */
      Peak(const double m, Pchargemask c=0):m_moz(m){
        m_chargemask=c;
        m_charges=NULL;
      }
    /**
       * build a PEak, reading charges from a string (see string2chargemask function to see how this works)
       * @param m
       * @param s
     */
      Peak(const double m, const string& s):m_moz(m){
        m_chargemask=string2chargemask(s);
        m_charges=NULL;
      }

    /**
       * copy contructor (reset the charges list to NULL)
       * @param p the template peak
     */
      Peak(const Peak& p):m_moz(p.m_moz){
        m_chargemask=p.m_chargemask;
        m_charges=NULL;
      }

      ~Peak(){
      }

    /**
       * converts a chargemask  value into a string
       * this string can be reversed by string2chargemask
       * 0 => ?
       * 12 => 2,3
       * @param mask the chargemask
       * @return a string
     */
      static string chargemask2string(const Pchargemask mask);

    /**
       * soes the oposite of chargemask2string, converting a stirng into a charge mask
       * "?" or "" => 0
       * "2,3", "2 3", "2+, 3+", "2+ OR 3+", "2+ AND 3+" etc. => 12
       * in fact, all what is not an integer is ignored
       * @param s
       * @return
     */
      static Pchargemask string2chargemask(const string& s);

      bool operator<(const Peak& p)const{
        return moz()<p.moz();
      }
     static bool ltptr(const Peak* p1, const Peak*p2){
       return p1->moz()<p2->moz();
     }

      friend ostream& operator<<(ostream& out, const Peak& p);
      friend ostream& operator<<(ostream& out, const Peak* p);

  /**
       *
       * @return m/z value
   */
      double moz() const {
        return m_moz;
      }

  /**
       *
       * @param theValue the mask to set
   */
      void setChargemask(const Pchargemask& theValue) {
        m_chargemask = theValue;
        if(m_charges)
          delete m_charges;
        m_charges=NULL;
      }
  

  /**
      The charge_S_ are stored in a mask. If bit i is set, then the peak is charged i+.
      For example chargemask=12 induces the peak is charged 2+ or 3+
      chargemask=0 means charge is unknown
       * @return the charges mask
   */
      Pchargemask chargemask() const {
        return m_chargemask;
      }

  /**
       * charges can be returned as a list of int. This list is created the first time it is call (NULL otherwise), the last value is 0 to terminate the list
       * @return the list of charges, 0 terminated
   */
      unsigned int *charges() {
        if(m_charges==NULL)
          buildChargesList();
        return m_charges;
      }
  

  };

#ifdef HAVE_PROTOBUF
  static inline bool operator<< (protobuf::RunLcmsms::Spectrum::Peak& pb, const class Peak& p) {
    pb.set_moz(p.moz());
    if (p.chargemask()) 
      pb.set_chargemask(p.chargemask());
    return true;
  }

  static inline bool operator>> (const protobuf::RunLcmsms::Spectrum::Peak& pb, class Peak& p) {
    // moz is set by constructor when peak is allocated
    if (pb.has_chargemask()) 
      p.setChargemask((Pchargemask) pb.chargemask());
    return true;
  }
#endif

}

#ifdef HAVE_BOOST_SERIALIZATION
#if defined(WIN32) && (BOOST_VERSION < 103600)
// other wise Visual Studio spits error messages
#include <boost/serialization/extended_type_info_typeid.hpp>
#endif
BOOST_CLASS_EXPORT_GUID(bol::Peak, "Peak") // register GUID
#endif

#endif
